#include "amever.h"
#include "map.h"
#include "packets.h"
#include "utils.h"

int __attribute__ ((format (printf, 2, 3))) client_setResponse(struct client *me, const char *fmt, ...)
{
	if ( me->wbuffer->size - me->wbuffer->mark < 255 && buffer_realloc(me->wbuffer, (me->wbuffer->size/16)+16)) {
		amelog("Bogus or spoofing or laggy client requiring too much wbuffer. Shutting it.\n");
		me->eof = 1;
		return;
	}
    va_list ap;
    va_start(ap, fmt);

	int size = (vsprintf(me->wbuffer->data+me->wbuffer->mark, fmt, ap));

	if (me->wbuffer->data[me->wbuffer->mark+2] == '$' && me->wbuffer->data[me->wbuffer->mark+3] == '$') {
		if (size > 65534)
			return;
		WFIFOW(me, 2) = size;
	}
	else if (me->wbuffer->data[me->wbuffer->mark+2] == '$') {
		if (size > 254)
			return;
		WFIFOB(me, 2) = size;
	}
	me->wbuffer->mark += size;
 va_end(ap);

 return size;
}

inline void client_writeln(struct client *me, const char *message) {
	WFIFOW(me, 0) = 0;
	WFIFOB(me, 2) = sprintf(WFIFOP(me, 3), "%s", message);
	WFIFOSP(me, WFIFOB(me, 2));
	return;
}
//
struct client *client_init (int fd) {
	amelog("Initing a client\n");
	struct client *me = (struct client *)malloc(sizeof(struct client));
	if (!me)
		return 0;

	if (server_registerFD(fd, FD_CLIENT, me) < 0) {
		amelog("Couldn't register client.\n");
		free(me);
		return 0;
	}
	memset(me, 0, sizeof(struct client));
	me->fd = fd;

	if (!(me->rbuffer = buffer_init(1024))) {
		amelog("Couldn't malloc rbuffer.\n");
		free(me);
		return 0;
	}
	if (!(me->wbuffer = buffer_init(1024))) {
		amelog("Couldn't malloc wbuffer.\n");
		buffer_shut(me->rbuffer);
		free(me);
		return 0;
	}
	me->achar = 0;
	me->userid = 0;
	strcpy(me->name, "+");
	WFIFOPR(me, 2);
	WFIFOW(me, 0) = 10;
	WFIFOSP(me, 2);
	return me;
}

void client_shut (struct client *me) {
	amelog("Shutting client ...\n");

	if (me == 0) 
		return;
	if (me->wbuffer->mark > 0 && me->net.writable) {
		return;
	}
	
	network_shutdown(me->fd);
	if (me->achar) {
		client_leaveMap(me);
		char_shut(me);
	}
	if (me->global_hook) {
		chain_nodeShut(me->global_hook);
	}

	if (me->rbuffer != 0) {
		buffer_shut(me->rbuffer);
	}
	if (me->wbuffer != 0) {
		buffer_shut(me->wbuffer);
	}
	if (me != 0) {
		amelog("Freeing me...\n");
		free(me);
	}
	me = 0;
	return;
}

int client_atcommand (struct client *me, const char *command) {

	return 0;
}

inline void client_chats(struct client *me) {
	amelog("In client chats...\n");
	if (s.status.shutting || !me || me->rbuffer->size <= 3 || !me->achar) {
		amelog("Bogus chat.\n");
		return;
	}
	int code = RFIFOW(me, 0);
	if (code < 2 || code > 9)
		code = (unsigned short)2;
	char tmp[128];
	memset(tmp, 0, 128);
	strncpy(tmp, RFIFOP(me, 3), 128);
	int size = 5 + strlen(tmp);
	struct chain_set *receivers = chain_setInit(0, 0, 255);
	if (map_foreach (WO_CHAR, me->achar->cell, 8, receivers) > 0)  {
		struct chain_node *noderator = receivers->first;
		struct client *c;
		while (noderator) {
			c = ((struct world_char *)noderator->val)->client;
			if (c != me && c && c->fd && c->wbuffer && c->eof != 1) {
				WFIFOPR(c, size);
				WFIFOW(c, 0) = code;
				WFIFOB(c, 2) = size;
				WFIFOW(c, 3) = me->achar->id;
				memcpy(WFIFOP(c, 5), tmp, size-5);
				WFIFOSP(c, size);
			}
			noderator = noderator->next;
		}
	}
	else {
		amelog("Nobody's near\n");
	}
	chain_setShut(receivers);
	return;
}

int client_login(struct client *me) {
	amelog("In client login...\n");
	char login[16];
	char pass[16];
	if (sscanf(RFIFOP(me, 3), "%[a-z0-9] %[a-z0-9]", login, pass) < 2) {
		amelog("Wrong arguments [ %s ]for login, shutting client.\n", RFIFOP(me, 3));
		return -1;
	}
	sprintf(s.buffy, "SELECT id FROM user WHERE login = '%s' AND pass = '%s' AND valid = 'valid' LIMIT 1", login, pass);
	int id = 0;

	if(mysql_query(&s.mysql_h, s.buffy) ) {
			amelog("DB server Error - %s\n", mysql_error(&s.mysql_h) );
	}
 	int access = 0;
	if (s.sql_res = mysql_store_result(&s.mysql_h)) {
		if (mysql_num_rows(s.sql_res)) {
			s.sql_row = mysql_fetch_row(s.sql_res);
			id = atoi(s.sql_row[0]);
		}
		mysql_free_result(s.sql_res);
	}

 if (id) {
	 if (!s.clients) {
		 s.clients = chain_setInit(0, 0, MAX_CLIENTS); //
	 }
	 else if (s.clients->len > s.clients->maxlen) {
		 amelog("MaxLen reached, cannot init another client.\n");
		 me->eof = 1;
		 return -1;
	 }


	 struct chain_node *node = chain_findExactIndex(s.clients, id);
	 if (node) {
		amelog("It seems like there's already a client with this userid, setting the ->eof\n");
		*((unsigned short *)me->wbuffer->data+(0)) = 0x0C;
		me->wbuffer->mark+=2;
		me->eof = 1;
		// Setting eof to the existing client too.
		((struct client *)(node->val))->eof = 1;
		return -1;
	 }
	 me->global_hook = chain_nodeInit(s.clients, me, id, 0);

 	 amelog("Authorization successfull, C_READY.\n");
	 WFIFOW(me, 0) = 0x0B;
	 WFIFOSP(me, 2);

	 char tmp[128];
	 me->userid = id;
	 me->logged = 1;
	 return 0;
	}
 else {
	 	amelog("No user found\n");
		WFIFOW(me, 0) = 0x0C;
		WFIFOSP(me, 2);
		return -1;
	}
}

int client_enterMap(struct client *me, int map_id, int x, int y) {
	if (!me->achar)
		char_init(me);
	struct world_map *wmap = s.maps[map_id].p;
	if (wmap == NULL) {
		amelog("Map not found.\n");
		return 1;
	}
	if (x > wmap->width || y > wmap->height) {
		amelog("client_enterMap: Spot out of range of the map.\n");
		return 1;
	}

	if (!me->achar)  {
		amelog("No char ???\n");
		return 0;
	}
	if (me->achar->id == 1)
		me->achar->cell = wmap->cells[0][2];
	else
		me->achar->cell = wmap->cells[y][x];

	if (me->achar->cell->objects == 0) {
		me->achar->cell->objects = chain_setInit(0, 0, 32);
	}
	me->achar->position = chain_nodeInit(me->achar->cell->objects, (void *)me->achar, WO_CHAR, 0);
	WFIFOPR(me, 14);
	WFIFOW(me, 0) = 40;
	WFIFOW(me, 2) = me->achar->cell->X;
	WFIFOW(me, 4) = me->achar->cell->Y;
	strcpy(WFIFOP(me, 6), me->achar->cell->wmap->name);
	WFIFOSP(me, 14);

	return 0;
}

int client_pushMove	(struct client *me, int x, int y) {
	chain_nodeInit(me->achar->steps, me->achar->cell->wmap->cells[y][x], 1, 0);
}

void client_leaveMap (struct client *me) {
	amelog("in leaveMap\n");
	if (me && me->achar && me->achar->cell) {
		struct chain_set *tmp = chain_setInit(0,0,255);
		if (map_foreach(WO_CHAR, me->achar->cell, CHAR_RADIUS, tmp)) {
			struct chain_node *node;
			for (node=tmp->first; node != 0;node=node->next) {
				if (node == me->achar->position)
					continue;
				if (CHAR(node->val) && CHAR(node->val)->client) {
					WFIFOPR(CHAR(node->val)->client, 4);
					WFIFOW(CHAR(node->val)->client, 0) = 29;
					WFIFOW(CHAR(node->val)->client, 2) = me->achar->id;
					WFIFOSP(CHAR(node->val)->client, 4);
				}
			}
	}
	/*
	if (me->achar->global_hook)
		chain_nodeShut(me->achar->global_hook);
	if (me->achar->position)
		chain_nodeShut(me->achar->position);
	if (me->achar->steps)
		chain_setShut(me->achar->steps);
*/
	}

	amelog("outa leaveMap\n");
	return;
}

int client_goto (int x, int y) {

}


int client_do (struct client *me) {
	/* What has to be done with him/her */
 if (me->wbuffer->mark > 0 && me->net.writable) {
 	if (me->eof)
 		printf("Writing never mind the eof.\n");
	 if (client_write(me) < 0) {
		 amelog("Writing returned less than ZERO, setting eof\n");
		 return 1;
	 }
 }
 
 if (me == NULL || me->eof) {
		amelog("client->eof is set\n");
		return 1;
 }
 
 if (me->net.readable) {
		s.fds[me->fd].idle = 0;
		if (netParser(me)) { // get data
			amelog("netParser returned error, shutting client\n");
			return 1;
		}
 }
 else if (me->rbuffer->mark > 1) {
	me->eof = netPacketParser(me); // analyze it
	return 0;
 }
 else if (me->achar && me->achar->status.action != CHA_STAYINGBY && me->achar->status.nextAction <= s.clock) {
		char_do(me->achar);
 }
 else {
		// Seems like iddle.
		if (++ s.fds[me->fd].idle == 0) {
			WFIFOPR(me, 3);
			WFIFOW(me, 0) = 0;
			WFIFOB(me, 2) = 3;
			WFIFOSP(me, 3);
 		}
	}
	return 0;
}

int client_write (struct client *me) {
    buffer_write(me->wbuffer, me->fd);
}

struct client *client_fromCharId(unsigned short id) {
	struct chain_node *node = chain_findIndex(s.clients, id);
	if (!node || node->index != id)
		return 0;
	return node->val;
}
